﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace VirtualDisk
{
    class MyString 
    {
        public static readonly MyString Empty;

        static MyString()
        {
            Empty = new MyString("");
        }
        private readonly char[] data;
        private readonly int count;
        public MyString(char[] value)
        {
            this.count = value.Length;
            this.data = new Char[count];
            Array.Copy(value, this.data, count);
        }

        public MyString(string value)
        {
            this.count = value.Length;
            this.data = new Char[count];
            Array.Copy(value.ToCharArray(), this.data, count);
        }

        public MyString(char[] value, int startIndex, int length)
        {
            if (startIndex < 0)
            {
                throw new ArgumentOutOfRangeException(startIndex.ToString());
            }
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(length.ToString());
            }
          
            if (startIndex+length >value.Length)
            {
                throw new ArgumentOutOfRangeException((startIndex + length).ToString());
            }
            this.count = length;
            this.data = new Char[count];
            Array.Copy(value, startIndex, this.data, 0, count);
        }

        public int Length
        {
            get { return count; }
        }

        public char this[int index]
        {
            get
            {
                if (index >= count || index < 0)
                {
                    throw new IndexOutOfRangeException();
                }
                else
                    return data[index];
            }
        }

        public static bool operator !=(MyString a, MyString b)
        {
            return !(a == b);
        }

        public static bool operator ==(MyString a, MyString b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }

            int n = a.Length;
            if (a.Length == b.Length)
            {
                char[] v1 = a.data;
                char[] v2 = b.data;
                int i = 0;
                int j = 0;
                while (n-- != 0)
                {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }

            return false;
        }

        public static MyString operator +(MyString a, MyString b)
        {
            return Concat(a, b);
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            MyString m = obj as MyString;
            if ((object)m == null)
            {
                return false;
            }

            int n = count;
            if (n == m.Length)
            {
                char[] v1 = data;
                char[] v2 = m.data;
                int i = 0;
                int j = 0;
                while (n-- != 0)
                {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }

            return false;


        }

        public bool Equals(MyString value)
        {
            if ((object)value==null)
            {
                return false;
            }
            int n = count;
            if (n == value.Length)
            {
                char[] v1 = data;
                char[] v2 = value.data;
                int i = 0;
                int j = 0;
                while (n-- != 0)
                {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }

            return false;
        }

        public override int GetHashCode()
        {
            int h = 0;
            char[] val = data;
            int len = count;
            for (int i = 0; i < len; i++) {
                h = 31*h + val[i];
            }
            return h;
        }
        public int IndexOf(char value)
        {
            return IndexOf(value, 0);
        }

        public int IndexOf(char value, int startIndex)
        {
            if ((object)value==null)
            {
                throw new ArgumentNullException();
            }
            if (startIndex < 0||startIndex>=count)
            {
                throw new ArgumentOutOfRangeException(startIndex.ToString());
            }
            for (int i = startIndex; i < count; i++)
            {
                if (data[i] == value)
                {
                    return i;
                }
            }
            return -1;    
        }

        public int IndexOfAny(char[] anyOf)
        {
            return IndexOfAny(anyOf, 0);
        }

        public int IndexOfAny(char[] anyOf, int startIndex)
        {
            if (anyOf == null)
            {
                throw new ArgumentNullException();
            }
            if (startIndex < 0 || startIndex >= count)
            {
                throw new ArgumentOutOfRangeException(startIndex.ToString());
            }

            for (int i = startIndex; i < count; i++)
            {
                for (int j = 0; j < anyOf.Length; j++)
                {
                    if (data[i] == anyOf[j])
                    {
                        return i;
                    }
                }
            }
            return -1;   

        }

        public static bool IsNullOrEmpty(MyString value)
        {
            if (value == null || value.Length == 0)
                return true;
            else
                return false;
        }

        public MyString Substring(int startIndex)
        {
            return Substring(startIndex, count-startIndex);
        }

        public MyString Substring(int startIndex, int length)
        {
            if (startIndex < 0)
            {
                throw new ArgumentOutOfRangeException(startIndex.ToString());
            }
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(length.ToString());
            }

            if (startIndex + length > count)
            {
                throw new ArgumentOutOfRangeException((startIndex + length).ToString());
            }
            return ((startIndex == count) && (length == 0)) ? Empty :
                new MyString(data, startIndex, length);
        }

        public MyString ToLower()
        {
            return ToLower(CultureInfo.CurrentCulture);
        }

        public MyString ToLower(CultureInfo culture)
        {
            char[] c = new char[count];
            for (int i = 0; i < count; i++)
            {
                c[i] = culture.TextInfo.ToLower(data[i]);
            }
            return new MyString(c);
        }

        public MyString ToUpper()
        {
            return ToUpper(CultureInfo.CurrentCulture);
        }

        public MyString ToUpper(CultureInfo culture)
        {
            char[] c = new char[count];
            for (int i = 0; i < count; i++)
            {
                c[i] = culture.TextInfo.ToUpper(data[i]);
            }
            return new MyString(c);
        }

        public override string ToString()
        {
            string str = "";
            for (int i = 0; i < count; i++)
            {
                str += data[i].ToString();
            }
            return str;
        }

        public MyString TrimStart()
        {
            int len = count;
            int st = 0;

            char[] val = data;   

            while ((st < len) && (val[st] <= ' '))
            {
                st++;
            }
            return (st > 0)  ? Substring(st, len - st) : this;

        }
        public MyString Trim()
        {
            int len = count;
            int st = 0;
               
            char[] val = data;   

            while ((st < len) && (val[st] <= ' '))
            {
                st++;
            }
            while ((st < len) && (val[len - 1] <= ' '))
            {
                len--;
            }
            return ((st > 0) || (len < count)) ? Substring(st, len-st) : this;
        }

        public MyString[] Split(params char[] separator)
        {
            return Split(separator, Int32.MaxValue, StringSplitOptions.None);
        }
        public MyString[] Split(char[] separator, int count)
        {
            return Split(separator, count, StringSplitOptions.None);
        }
        public MyString[] Split(char[] separator, StringSplitOptions options)
        {
            return Split(separator, Int32.MaxValue, options);
        }
        public MyString[] Split(char[] separator, int count, StringSplitOptions options)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(count.ToString());
            }
                char[] ch = separator;
                if (ch == null || ch.Length == 0)
                {
                    ch = new Char[] { ' ' };
                }
                int off = 0;
                int next = 0;
                List<MyString> list = new List<MyString>();
                while (off<this.count&&(next = IndexOfAny(ch, off)) != -1)
                {
                    if (list.Count < count - 1)
                    {
                        if (StringSplitOptions.RemoveEmptyEntries != options || Substring(off, next - off).Length != 0)
                        {
                            list.Add(Substring(off, next - off));
                        }
                        off = next + 1;
                    } else {
                        if (StringSplitOptions.RemoveEmptyEntries != options || Substring(off, this.count - off).Length != 0)
                        {
                            list.Add(Substring(off, this.count - off));
                        }
                        off = this.count;
                        break;
                    }
                }


                if (list.Count < count)
                {
                    if (StringSplitOptions.RemoveEmptyEntries != options || Substring(off,this.count - off).Length != 0)
                    {
                        list.Add(Substring(off, this.count - off));
                    }
                }
                return list.ToArray();
           
        }

        public static MyString Concat(MyString str0, MyString str1)
        {
            int len=str0.Length;
            int otherLen = str1.Length;
            if (otherLen == 0) 
            {
                return str0;
            }
            if (len == 0)
            {
                return str1;
            }
            char[] buf = new char[len + otherLen];
            Array.Copy(str0.data, 0, buf, 0, len);
            Array.Copy(str1.data, 0, buf, len, otherLen);
            return new MyString(buf);
            }

        public bool Contains(char value)
        {
            if (IndexOf(value) != -1)
            {
                return true;
            }
            else
                return false;
        }

       
    }

    
    
}
